/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Control;

import Engine.Engine;
import Engine.KeyActions;
import Engine.KeyControl;
import Engine.KeyManager;
import Engine.MenuActions;
import Engine.MouseManager;
import Engine.Renderer;
import Engine.Setup;
import Highscore.Highscore;
import Highscore.Score;
import Lights.Light;
import Menus.GameOver;
import Menus.HighScoreMenu;
import Menus.LoadMenu;
import Menus.MainMenu;
import Menus.Menu;
import Menus.PauseMenu;
import Menus.SaveMenu;
import ObjectPack.MovablePack;
import ObjectPack.PlayerPack;
import ObjectPack.Save;
import Players.Player;
import Ships.Ship;
import com.sun.opengl.util.GLUT;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.TreeMap;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.swing.JOptionPane;

/**
 *
 * @author Gustavo Buzogany Eboli
 */
public class GameController implements GLEventListener {
    private static GameController myInstance;
    private KeyManager evListener;
    private KeyControl actionHandler;
    private GLAutoDrawable drawableRef;
    private Setup conf;
    private Engine engine;
    private Renderer renderer;
    private boolean clicked;
    private Menu currentMenu;
    private ArrayList<Light> lightList;
    private int mousex, mousey;
    private boolean paused;
    private int mode;
    private Highscore hs;
    private SaveManager sm;

    /**
     * Metodo construtor do controlador (privado - singleton)
     */
    private GameController()
    {
        conf = new Setup();
        renderer = Renderer.getInstance();
        actionHandler = MenuActions.getInstance();
        lightList = new ArrayList<Light>();
        mode = 1;
        hs = new Highscore();
        sm = new SaveManager();
    }

    /**
     * Metodo getInstance (Singleton)
     * @return Retorna ponteiro pra instancia
     */
    public static GameController getInstance()
    {
        if(myInstance == null)
        {
            myInstance = new GameController();
        }
        return myInstance;
    }

    /**
     * Alterna entre pausado e ativo
     */
    public void switchPaused()
    {
        if(paused == false)
        {
            switchToMenu();
            renderer.setMode(2);
            paused = true;
        } else {
            switchToGame();
            renderer.setMode(0);
            paused = false;
            KeyManager km = actionHandler.getMaster();
            km.keyReleased('p');
        }
    }

    /**
     * Metodo de inicializacao da OpenGL
     * @param drawable
     */
    public void init(GLAutoDrawable drawable) {

        drawableRef = drawable;
        GLUT glut = new GLUT();

        conf.initGL(drawable, glut);
        conf.initLights(drawable, lightList);

        renderer.setDrawable(drawable);
        renderer.loadModel();

        MouseManager mmanager = new MouseManager(Engine.getInstance(), GameController.getInstance());
        mmanager.setRenderer(renderer);
        drawable.addMouseListener(mmanager);
        drawable.addMouseMotionListener(mmanager);

        currentMenu = new MainMenu(GameController.getInstance());

        renderer.setMenu(currentMenu);
    }

    /**
     * Metodo de desenho da JOGL
     * @param drawable
     */
    public void display(GLAutoDrawable drawable) {

        if(clicked == true)
        {
            renderer.processClick();
            clicked = false;
        } else {
            renderer.processMousePosition(mousex, mousey, currentMenu);
        }

        actionHandler.pool();

        if(mode == 1)
        {
            renderer.draw();
        } else {
            if(paused == false) {
                engine.step();
            }
            renderer.draw();
        }
    }

    /**
     * M?todo chamado quando a janela ? redimensionada.
     * @param drawable refer?ncia do GLAutoDrawable.
     * @param x Posi??o X
     * @param y Posi??o Y
     * @param width Largura
     * @param height Algura
     */
    public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
        renderer.reshape(x, y, width, height);
    }

        /**
     * M?todo chamado quando a resolu??o ? alterada.
     * @param drawable refer?ncia do GLAutoDrawable.
     * @param modeChanged Modo alterado.
     * @param deviceChanged Dispositivo Alterado.
     */
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }

    /**
     * Manipula a posicao do mouse
     * @param x
     * @param y
     */
    public void mouseMoved(int x, int y) {
        mousex = x;
        mousey = y;
    }

    /**
     * Muda o estado de "clicked" para o Ray Casting
     */
    public void setClicked() {
        clicked = true;
    }

    /**
     * Inicia uma nova partida
     */
    void newGame() {

        Engine.killEngine();
        engine = Engine.getInstance();
        engine.init(drawableRef);
        engine.setGameController(this);

        switchToGame();

        mode = 0;
        PauseMenu pm = new PauseMenu(this);
        currentMenu = pm;
        renderer.setEngine(engine);
        renderer.setMenu(pm);
        renderer.setMode(0);
        paused = false;

        KeyManager km = actionHandler.getMaster();
        km.keyReleased('p');
    }

    /**
     * Muda o actionHandler para o "KeyActions". Assim, os eventos de teclado sao relacionados a movimentacao do jogador.
     */
    private void switchToGame()
    {
        KeyManager km = actionHandler.getMaster();

        km.setActionHandler(KeyActions.getInstance());

        actionHandler = KeyActions.getInstance();
    }

    /**
     * Muda o actionHandler para "MenuActions". Assim os eventos de teclado sao relacionados a pausar/despausar.
     */
    private void switchToMenu()
    {
        KeyManager km = actionHandler.getMaster();

        km.setActionHandler(MenuActions.getInstance());

        actionHandler = MenuActions.getInstance();
    }

    /**
     * Cria uma nova entrada no highscore, e redireciona o usuario para a tela de gameOver
     */
    public void playerKilled() {
        switchToMenu();
        String playerName = JOptionPane.showInputDialog("Digite seu nome:");

        while(playerName == null)
        {
                playerName = JOptionPane.showInputDialog("Digite seu nome:");
        }

        Score newScore = new Score(engine.getHumanPlayer().getExp(), playerName);

        hs.addScore(newScore);
        hs.writeList();

        GameOver go = new GameOver(this);

        renderer.setMenu(go);
        currentMenu = go;
        renderer.setMode(3);
    }

    public Highscore getHs() {
        return hs;
    }

    public SaveManager getSm() {
        return sm;
    }

    /**
     * Redireciona o usuario para a tela de "Save"
     */
    public void saveScreen() {
        SaveMenu nsm = new SaveMenu(this);

        currentMenu = nsm;
        renderer.setMenu(nsm);
    }

    /**
     * Redireciona o usuario para a tela de "Load".
     * @param origin De onde o metodo foi chamado. 0 = menu principal, 1 = inGame
     */
    public void loadScreen(int origin) {
        LoadMenu lm = new LoadMenu(this, origin);

        currentMenu = lm;
        renderer.setMenu(lm);
    }

    /**
     * Redireciona o usuario para a tela de "pause"
     */
    public void backToPause()
    {
        PauseMenu pm = new PauseMenu(this);
        currentMenu = pm;
        renderer.setMenu(pm);
    }

    /**
     * Redireciona o usuario para a tela de Highscore
     * @param b De onde o metodo foi chamado. False = menu principal, True = ingame
     */
    public void showHighscores(boolean b) {
        HighScoreMenu hsm = new HighScoreMenu(this, b);
        currentMenu = hsm;
        renderer.setMenu(hsm);
    }

    /**
     * Redireciona o usuario para o menu principal
     */
    public void showMainMenu() {
        currentMenu = new MainMenu(GameController.getInstance());
        renderer.setMenu(currentMenu);
    }

    /**
     * Salva o jogo
     * @param i Slot a ser salvo
     */
    public void saveGame(int i) {
        Save s = new Save();
        s.now   = timeUtil("h:mm a");
        s.today = timeUtil("MM/dd/yy");

        Player player = engine.getHumanPlayer();
        Ship playerShip = player.getMyShip();

        MovablePack pShip = new MovablePack();
        PlayerPack pPlayer = new PlayerPack();

        pShip.euler = playerShip.getEuler_ang();
        pShip.health = playerShip.getHealth();
        pShip.maxPower = playerShip.getMaxPower();
        pShip.maxhealth = playerShip.getHealth_max();
        pShip.pos = playerShip.getPos();
        pShip.power = playerShip.getPower();
        pShip.shieldsPower = playerShip.getShieldsPower();
        pShip.speed = playerShip.getSpeed();
        pShip.weaponsPower = playerShip.getWeaponsPower();

        pPlayer.enemies = engine.getEnemiesCount();
        pPlayer.exp = player.getExp();
        pPlayer.level = player.getLevel();

        s.slot = i;
        s.mpack = pShip;
        s.ppack = pPlayer;

        sm.addSave(s);
        sm.writeList();

        saveScreen();
    }

    /**
     * Utilitario para lidar com o tempo
     * @param dateFormat Formato da data/hora pretendido.
     * @return String no formato informado.
     */
    public String timeUtil(String dateFormat) {
        Calendar cal = Calendar.getInstance();
        SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
        return sdf.format(cal.getTime());
    }

    /**
     * Carrega o jogo de um determinado slot.
     * @param slot Qual slot a ser carregado
     */
    public void loadGame(int slot)
    {
        TreeMap saves = sm.getListaSaves();

        Save s = (Save) saves.get(slot);

        newGame();
        engine.setEnemies(s.ppack.enemies);
        Player player = engine.getHumanPlayer();
        Ship ship = player.getMyShip();

        player.setExp(s.ppack.exp);
        player.setLevel(s.ppack.level);

        ship.setHealth(s.mpack.health);
        ship.setMaxPower(s.mpack.maxPower);
        ship.setHealth_max(s.mpack.maxhealth);
        ship.setPower(s.mpack.power);
        ship.setWeaponsPower((int) s.mpack.weaponsPower);
        ship.setShieldsPower((int) s.mpack.shieldsPower);
        ship.setPos(s.mpack.pos);
        ship.setSpeed(s.mpack.speed);
        ship.setEuler_ang(s.mpack.euler);

        
        engine.generateEnemies();
    }
}
